package com.heima.article.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.heima.article.entity.ApArticle;
import com.heima.article.entity.ApHotArticles;
import com.heima.article.mapper.ApHotArticlesMapper;
import com.heima.article.service.ApArticleService;
import com.heima.article.service.ApHotArticlesService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.heima.common.util.BeanHelper;
import com.heima.common.util.JsonUtils;
import com.heima.model.article.dtos.ArticleBehaviorStreamMsg;
import com.heima.model.article.dtos.ArticleDto;
import com.heima.model.article.dtos.HotArticleVo;
import lombok.extern.slf4j.Slf4j;
import org.joda.time.DateTime;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.BoundHashOperations;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.lang.invoke.LambdaMetafactory;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * <p>
 * 热文章表 服务实现类
 * </p>
 *
 * @author HM
 * @since 2023-02-15
 */
@Slf4j
@Service
public class ApHotArticlesServiceImpl extends ServiceImpl<ApHotArticlesMapper, ApHotArticles> implements ApHotArticlesService {


    @Autowired
    private ApArticleService apArticleService;
    /**
     * 频道 新闻列表
     */
    private final String PRE_CHANNEL_FIX = "ld:article:hot:cid:";

    /**
     * 文章列表
     */
    private final String PRE_ARTICLE_FIX = "ld:article:aid";

    /**
     * 定时计算文章分值
     */
    @Override
    public void computeHotArticle() {

//        计算5天前的时间
        String myTime = DateTime.now().minusDays(5).toString("yyyy-MM-dd 00:00:00");
        log.info("myTime={}",myTime);
//        设置查询条件
        LambdaQueryWrapper<ApArticle> queryWrapper = new LambdaQueryWrapper<>();
        queryWrapper.
                eq(ApArticle::getIsDown,false).
                eq(ApArticle::getIsDelete,false).
                gt(ApArticle::getPublishTime,myTime);
//        查询5天内的文章
        List<ApArticle> apArticleList = apArticleService.list(queryWrapper);
        if(CollectionUtils.isEmpty(apArticleList)){
            log.info("没有数据，不用计算，结束");
            return ;
        }
        log.info("apArticleList={}",apArticleList);
//      把ApArticle 转 HotArticleVo 对象属性拷贝
        List<HotArticleVo> hotArticleVoList = BeanHelper.copyWithCollection(apArticleList, HotArticleVo.class);
//        计算文章分值
        computeArticleScore(hotArticleVoList);
//        存储文章分值
        cacheHotArticleVo(hotArticleVoList);
    }

    /**
     * 推荐频道，从Redis查询
     * @return
     */
    @Override
    public List<ArticleDto> getHotArticle() {

//        从zset 根据分值排序倒叙查询 文章id
        Set<String> articleIds = redisTemplate.opsForZSet().reverseRange(PRE_CHANNEL_FIX+"0", 0, -1);
        if(CollectionUtils.isEmpty(articleIds)){
            return null;
        }
        log.info("articleIds={}",articleIds);
//        从hash根据文章id 查询文章内容,并没有访问redis
        BoundHashOperations<String, String, String> boundHashOps = redisTemplate.boundHashOps(PRE_ARTICLE_FIX);
        List<String> valueList = boundHashOps.multiGet(articleIds);
        if(CollectionUtils.isEmpty(valueList)){
            return null;
        }
//        把json字符串集合转对象集合
//        List<ArticleDto> list = new ArrayList<>();
//        for (String jsonStr : valueList) {
//            ArticleDto articleDto = JsonUtils.toBean(jsonStr, ArticleDto.class);
//            list.add(articleDto);
//        }
        List<ArticleDto> list = valueList.stream().map(str -> {
            return JsonUtils.toBean(str, ArticleDto.class);
        }).collect(Collectors.toList());
        return list;
    }

    @Override
    public void computeArticleScoreRealTime(ArticleBehaviorStreamMsg streamMsg) {
        Long articleId = streamMsg.getArticleId();
//        计算分值
        HotArticleVo hotArticleVo = new HotArticleVo();
        hotArticleVo.setId(articleId);
        hotArticleVo.setViews(streamMsg.getViews());
        hotArticleVo.setLikes(streamMsg.getLikes());
        hotArticleVo.setComments(streamMsg.getComments());
        hotArticleVo.setCollection(streamMsg.getCollection());
        this.computeScore(hotArticleVo);
        log.info("hotarticleVo=={}",hotArticleVo);
//        当日分值 权重 *3
        Long score = hotArticleVo.getScore() * 3;
        log.info("score={}",score);
        hotArticleVo.setScore(score);
//        把计算出来的分值，设置到Redis中
        redisTemplate.opsForZSet().incrementScore(
                PRE_CHANNEL_FIX+"0",
                hotArticleVo.getId().toString(),
                hotArticleVo.getScore());

//        判断redis中是否存在文章信息
        Boolean b = redisTemplate.opsForHash().hasKey(PRE_ARTICLE_FIX, hotArticleVo.getId().toString());
        if(!b){
//            redis中没有文章的数据
//            先根据文章id查询文章数据
            ApArticle apArticle = apArticleService.getById(hotArticleVo.getId());
            hotArticleVo = BeanHelper.copyProperties(apArticle,HotArticleVo.class);
//            设置到redis中
            redisTemplate.opsForHash().put(
                    PRE_ARTICLE_FIX,
                    hotArticleVo.getId().toString(),
                    JsonUtils.toString(hotArticleVo));
        }
    }

    @Autowired
    private StringRedisTemplate redisTemplate;
    /**
     * 缓存文章分值和文章数据
     * @param hotArticleVoList
     */
    private void cacheHotArticleVo(List<HotArticleVo> hotArticleVoList) {
        for (HotArticleVo hotArticleVo : hotArticleVoList) {
//        把文章id 和分值存入  Zset  key  value  score
//            放入的是推荐频道的数据
            redisTemplate.opsForZSet().add(
                    PRE_CHANNEL_FIX+"0",
                    hotArticleVo.getId().toString(),
                    hotArticleVo.getScore());
//        把文章内容存入 Hash结构
            redisTemplate.opsForHash().put(
                    PRE_ARTICLE_FIX,
                    hotArticleVo.getId().toString(),
                    JsonUtils.toString(hotArticleVo));
        }

    }

    /**
     * 计算文章分值
     * @param hotArticleVoList
     */
    private void computeArticleScore(List<HotArticleVo> hotArticleVoList) {
        for (HotArticleVo hotArticleVo : hotArticleVoList) {
            computeScore(hotArticleVo);
            log.info("hotArticleVo={}",hotArticleVo);
        }
    }

    /**
     * 计算分数
     * @param hotArticleVo
     */
    private void computeScore(HotArticleVo hotArticleVo) {

        long score = 0;
//        阅读1分 ，点赞3分 ，评论5分，收藏8分
        if(hotArticleVo.getViews() != null){
            score +=  hotArticleVo.getViews();
        }
        if(hotArticleVo.getLikes() != null){
            score += hotArticleVo.getLikes() *3;
        }
        if(hotArticleVo.getComments() != null){
            score += hotArticleVo.getComments() *5;
        }
        if(hotArticleVo.getCollection() != null){
            score += hotArticleVo.getCollection() *8;
        }
        hotArticleVo.setScore(score);
    }
}
